package golf.stockinfo.buysellsimulation;

import golf.stockinfo.buysellsimulation.SimulationData.TransactionType;
import golf.stockinfo.buysellsimulation.util.DateUtil;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.Date;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.TreeMap;

import oracle.jdbc.OraclePreparedStatement;

import org.apache.commons.math.stat.regression.SimpleRegression;
import org.joda.time.DateTime;
import org.joda.time.Days;

/**
 * This class is to simulation the approach of referring to Weekly FX rate from
 * E-finance as a indicator and use the indicator to trade in Forex market.
 * 
 * @author golf
 * 
 */
public class SimulateSET_SET50_CombinedApproach implements Serializable {
	
	/**
	 * 
	 */
	private static final long serialVersionUID = 7209116279089872630L;

	public static int SMAV = 8;
	
	//The best cut loss is -13,000
	public static Date START_DATE = Date.valueOf("1996-11-28");
	public static int SIMULATION_LENGTH_IN_YEAR = 20;
	
	public static double TFEX_SET50_MARGIN_PER_CONTRACT = 38000;
	public static double TFEX_PRICE_PER_POINT = 1000;
	
	public static double LOSS_STOPPING_PER_CONTRACT = -45000;
	public static double PERCENT_MAX_LOSS_ON_BALANCE_TFEX = -0.1;
	public static double PERCENT_MAX_LOSS_ON_BALANCE_SET50_ETF = -0.05;
	public static double INITIAL_CAPITAL = 500000;
	public static double MINIMUM_RUNNING_BALANCE = 50000; //minimum balance to quite the trading.
	public static int CAPPED_NUMBER_OF_CONTRACT = 1;
	
	public static float TAKE_PROFIT_THRESHOLD = 0.05f; //this is the percent on investment to take profit. E.g. 0.2 means when the profit goes up to 20% of the running balance then sell the lots.
	public static boolean TAKE_PROFIT = true;
	
	public static boolean PLAY_SET50_ETF_ON_LONG = false;
	
	public static boolean CONTINUOUS_INVESTING = false;
	
	private static TreeMap<DateTime, MarketData> weeklyData;
//	private TreeMap<DateTime, MarketData> dailyData;
	private ArrayList<SimulationData> simulationDataArray = new ArrayList<SimulationData>();
	private static DecimalFormat numberFormat = new DecimalFormat("###,###.00");
	private static DecimalFormat interestFormat = new DecimalFormat("###,###.0000");
	private static DecimalFormat daysFormat = new DecimalFormat("###,###");
	
	private static boolean driverIsLoaded = false;
	
	private static SimpleRegression SIMPLE_REGRESSION = new SimpleRegression();

	private Date dateOfMaxLossForTheWeek = null;
	private Date dateOfMaxGainForTheWeek = null;

	
	public SimulateSET_SET50_CombinedApproach(){
		
		if(!driverIsLoaded){
			try {
				//Load Oracle JDBC driver
				DriverManager.registerDriver(new oracle.jdbc.driver.OracleDriver());
				DriverManager.registerDriver(new com.mysql.jdbc.Driver());
			} catch (SQLException e) {
				e.printStackTrace();
				throw new RuntimeException("SQL Error : " + e.getMessage());
			}
			driverIsLoaded = true;
		}
	}

	/**
	 * 
	 * 
	 * @param args
	 */
	public static void main(String[] args) {

//		singleSimulation();
		
		bulkSimulationDBLoad();
//		printSimulationOutputByCodnitions();
		
//		bulkSimulation2();
//		printBulkSimulationOutput();
	}
	
	public static void singleSimulation(){
		
		SimulateSET_SET50_CombinedApproach ths = new SimulateSET_SET50_CombinedApproach();
		ths.execute(true);
		
		System.out.println(ths.simulationDataArray.get(0).getStartDate() + ";" + ths.simulationDataArray.get(ths.simulationDataArray.size() - 1).getEndDate() + 
				";" + numberFormat.format(ths.simulationDataArray.get(ths.simulationDataArray.size() - 1).getBalance()));
		
		DateTime dt1 = new DateTime(ths.simulationDataArray.get(0).getStartDate());
		DateTime dt2 = new DateTime(ths.simulationDataArray.get(ths.simulationDataArray.size() - 1).getEndDate());
		
		System.out.println("======================================================");
		System.out.println("Summary");
		System.out.println("======================================================");
		System.out.println("Investment start date      : " + ths.simulationDataArray.get(0).getStartDate());
		System.out.println("Investment end date        : " + ths.simulationDataArray.get(ths.simulationDataArray.size() - 1).getEndDate());
		System.out.println("Investment time (days)     : " + daysFormat.format(Days.daysBetween(dt1, dt2).getDays()));
		System.out.println("Start Investment amount    : " + numberFormat.format(INITIAL_CAPITAL));
		System.out.println("Total amount of end balance: " + numberFormat.format(ths.simulationDataArray.get(ths.simulationDataArray.size() - 1).getBalance()));
		String roundOffPercent = Double.toString((getApproxEquivalentInterestPerYear(
				INITIAL_CAPITAL, 
				ths.simulationDataArray.get(ths.simulationDataArray.size() - 1).getBalance(), 
				Days.daysBetween(dt1, dt2).getDays())) * 100).split("\\.")[0];
		System.out.println("Approximate Interest/year  : " + roundOffPercent + "%");
		System.out.println("======================================================");

	}
	
	public static void bulkSimulation2(){
		
		SimulateSET_SET50_CombinedApproach ths = new SimulateSET_SET50_CombinedApproach();
		TreeMap<String, ArrayList<SimulationData>> totalSimResult = new TreeMap<String, ArrayList<SimulationData>>();
		
		Date[] dateArr = {Date.valueOf("1996-08-30"),
				Date.valueOf("1996-10-14"),
				Date.valueOf("1996-11-28"),
				Date.valueOf("1997-01-12"),
				Date.valueOf("1997-02-26"),
				Date.valueOf("1997-04-12"),
				Date.valueOf("1997-05-27"),
				Date.valueOf("1997-07-11"),
				Date.valueOf("1997-08-25"),
				Date.valueOf("1997-10-09"),
				Date.valueOf("1997-11-23"),
				Date.valueOf("1998-01-07"),
				Date.valueOf("1998-02-21"),
				Date.valueOf("1998-04-07"),
				Date.valueOf("1998-05-22"),
				Date.valueOf("1998-07-06"),
				Date.valueOf("1998-08-20"),
				Date.valueOf("1998-10-04"),
				Date.valueOf("1998-11-18"),
				Date.valueOf("1999-01-02"),
				Date.valueOf("1999-02-16"),
				Date.valueOf("1999-04-02"),
				Date.valueOf("1999-05-17"),
				Date.valueOf("1999-07-01"),
				Date.valueOf("1999-08-15"),
				Date.valueOf("1999-09-29"),
				Date.valueOf("1999-11-13"),
				Date.valueOf("1999-12-28"),
				Date.valueOf("2000-02-11"),
				Date.valueOf("2000-03-27"),
				Date.valueOf("2000-05-11"),
				Date.valueOf("2000-06-25"),
				Date.valueOf("2000-08-09"),
				Date.valueOf("2000-09-23"),
				Date.valueOf("2000-11-07"),
				Date.valueOf("2000-12-22"),
				Date.valueOf("2001-02-05"),
				Date.valueOf("2001-03-22"),
				Date.valueOf("2001-05-06"),
				Date.valueOf("2001-06-20"),
				Date.valueOf("2001-08-04"),
				Date.valueOf("2001-09-18"),
				Date.valueOf("2001-11-02"),
				Date.valueOf("2001-12-17"),
				Date.valueOf("2002-01-31"),
				Date.valueOf("2002-03-17"),
				Date.valueOf("2002-05-01"),
				Date.valueOf("2002-06-15"),
				Date.valueOf("2002-07-30"),
				Date.valueOf("2002-09-13"),
				Date.valueOf("2002-10-28"),
				Date.valueOf("2002-12-12"),
				Date.valueOf("2003-01-26"),
				Date.valueOf("2003-03-12"),
				Date.valueOf("2003-04-26"),
				Date.valueOf("2003-06-10"),
				Date.valueOf("2003-07-25"),
				Date.valueOf("2003-09-08"),
				Date.valueOf("2003-10-23"),
				Date.valueOf("2003-12-07"),
				Date.valueOf("2004-01-21"),
				Date.valueOf("2004-03-06"),
				Date.valueOf("2004-04-20"),
				Date.valueOf("2004-06-04"),
				Date.valueOf("2004-07-19"),
				Date.valueOf("2004-09-02"),
				Date.valueOf("2004-10-17"),
				Date.valueOf("2004-12-01"),
				Date.valueOf("2005-01-15"),
				Date.valueOf("2005-03-01"),
				Date.valueOf("2005-04-15"),
				Date.valueOf("2005-05-30"),
				Date.valueOf("2005-07-14"),
				Date.valueOf("2005-08-28"),
				Date.valueOf("2005-10-12"),
				Date.valueOf("2005-11-26"),
				Date.valueOf("2006-01-10"),
				Date.valueOf("2006-02-24"),
				Date.valueOf("2006-04-10"),
				Date.valueOf("2006-05-25"),
				Date.valueOf("2006-07-09"),
				Date.valueOf("2006-08-23"),
				Date.valueOf("2006-10-07"),
				Date.valueOf("2006-11-21"),
				Date.valueOf("2007-01-05"),
				Date.valueOf("2007-02-19"),
				Date.valueOf("2007-04-05"),
				Date.valueOf("2007-05-20"),
				Date.valueOf("2007-07-04"),
				Date.valueOf("2007-08-18"),
				Date.valueOf("2007-10-02"),
				Date.valueOf("2007-11-16"),
				Date.valueOf("2007-12-31"),
				Date.valueOf("2008-02-14"),
				Date.valueOf("2008-03-30"),
				Date.valueOf("2008-05-14"),
				Date.valueOf("2008-06-28"),
				Date.valueOf("2008-08-12"),
				Date.valueOf("2008-09-26"),
				Date.valueOf("2008-11-10"),
				Date.valueOf("2008-12-25"),
				Date.valueOf("2009-02-08"),
				Date.valueOf("2009-03-25"),
				Date.valueOf("2009-05-09"),
				Date.valueOf("2009-06-23"),
				Date.valueOf("2009-08-07"),
				Date.valueOf("2009-09-21"),
				Date.valueOf("2009-11-05"),
				Date.valueOf("2009-12-20"),
				Date.valueOf("2010-02-03"),
				Date.valueOf("2010-03-20"),
				Date.valueOf("2010-05-04"),
				Date.valueOf("2010-06-18"),
				Date.valueOf("2010-08-02"),
				Date.valueOf("2010-09-16"),
				Date.valueOf("2010-10-31"),
				Date.valueOf("2010-12-15"),
				Date.valueOf("2011-01-29"),
				Date.valueOf("2011-03-15"),
				Date.valueOf("2011-04-29"),
				Date.valueOf("2011-06-13"),
				Date.valueOf("2011-07-28"),
				Date.valueOf("2011-09-11"),
				Date.valueOf("2011-10-26"),
				Date.valueOf("2011-12-10"),
				Date.valueOf("2012-01-24"),
				Date.valueOf("2012-03-09"),
				Date.valueOf("2012-04-23"),
				Date.valueOf("2012-06-07"),
				Date.valueOf("2012-07-22"),
				Date.valueOf("2012-09-05")};
		
		int count=0;
		
		
		//Save the date array to output object for serialization.
		for (Date dt:dateArr){
			SimulationOutputData.DATE_ARRAY.add(dt);
		}
		
		// the first loop of c is to count the number of total case for ETA calculation
		for(int i=0;i<1;i++){
			double MaxPcntMaxLossOnETF = -0.50, StartPcntMaxLossOnETF = -0.05;
			if(i==0){
				SimulateSET_SET50_CombinedApproach.PLAY_SET50_ETF_ON_LONG = false;
				MaxPcntMaxLossOnETF = StartPcntMaxLossOnETF;
			}else{
				SimulateSET_SET50_CombinedApproach.PLAY_SET50_ETF_ON_LONG = true;
			}
			for(double pcntMaxLossOnETF = StartPcntMaxLossOnETF ; pcntMaxLossOnETF >= MaxPcntMaxLossOnETF; pcntMaxLossOnETF = pcntMaxLossOnETF - 0.05){
				for(int cappedContract = 500; cappedContract <= 500; cappedContract = cappedContract + 10){
					for(int smav = 5; smav <= 5; smav++){
						for(double minRunBal = 100000; minRunBal <= 100000; minRunBal = minRunBal + 25000){
							for(double pcntMaxLossOnBal = -0.1 ; pcntMaxLossOnBal >= -0.10; pcntMaxLossOnBal = pcntMaxLossOnBal - 0.1){
								for(double lossStopPerLot = -10000; lossStopPerLot >= -10000; lossStopPerLot = lossStopPerLot - 5000){
									ArrayList<SimulationData> sims = new ArrayList<SimulationData>();
									for (Date dt:dateArr){
										
										SimulateSET_SET50_CombinedApproach.PERCENT_MAX_LOSS_ON_BALANCE_SET50_ETF = pcntMaxLossOnETF;
										SimulateSET_SET50_CombinedApproach.CAPPED_NUMBER_OF_CONTRACT = cappedContract;
										SimulateSET_SET50_CombinedApproach.START_DATE = dt;
										SimulateSET_SET50_CombinedApproach.SMAV = smav;
										SimulateSET_SET50_CombinedApproach.LOSS_STOPPING_PER_CONTRACT = lossStopPerLot;
										SimulateSET_SET50_CombinedApproach.PERCENT_MAX_LOSS_ON_BALANCE_TFEX = pcntMaxLossOnBal;
										SimulateSET_SET50_CombinedApproach.MINIMUM_RUNNING_BALANCE = minRunBal;
										
										ths = new SimulateSET_SET50_CombinedApproach();

										ths.execute(false);

										SimulationData simDat = new SimulationData();
										try{
											simDat.setStartDate(ths.simulationDataArray.get(0).getStartDate());
											simDat.setEndDate(ths.simulationDataArray.get(ths.simulationDataArray.size() - 1).getEndDate());
											simDat.setBalance(ths.simulationDataArray.get(ths.simulationDataArray.size() - 1).getBalance());
//											simDat.setEndIndex(getApproxEquivalentInterestPerYear(INITIAL_CAPITAL, simDat.getBalance_asDouble(), Days.daysBetween(new DateTime(ths.simulationDataArray.get(0).getStartDate()), new DateTime(ths.simulationDataArray.get(ths.simulationDataArray.size() - 1).getEndDate())).getDays()));
										}catch(IndexOutOfBoundsException e){
											
										}
										sims.add(simDat);
										
										if(count % 10000 == 0){
											System.out.println(new Timestamp(System.currentTimeMillis()).toString() + " ; " + numberFormat.format(count) + " ; " + totalSimResult.size());
										}
										count++;
									}
									
									String type;
									if(PLAY_SET50_ETF_ON_LONG){
										type = "ETF";
									}else{
										type = "TFEX";
									}
									totalSimResult.put(type + ";" + cappedContract + ";" +
											minRunBal + ";"
											+ pcntMaxLossOnBal + ";"
											+ pcntMaxLossOnETF + ";"
											+ lossStopPerLot + ";"
											+ "SMAV-" + smav, sims);
									
								}
							}
						}
					}
				}
			}
		}
		
		//Print header
		System.out.print(
				"TradeType" +
				";cappedContract" +
				";MinBalance" +
				";PercentMaxLossOnBal" +
				";PercentMaxLossOnETF" +
				";LossStopPerLot" +
				";SMAV");
		for (Date dt:dateArr){
			System.out.print(";" + dt.toString());
		}
		System.out.println("");
		
		Iterator<String> itrStr = totalSimResult.keySet().iterator();
		while(itrStr.hasNext()){
			String key = itrStr.next();
			ArrayList<SimulationData> sims = totalSimResult.get(key);
			System.out.print(key);
			for(SimulationData sim:sims){
				System.out.print(";" + numberFormat.format(sim.getBalance()));
			}
			System.out.println("");
		}
		
//		SimulationOutputData.unload("/tmp/simulation.dat");
		
	}
	
	public static void bulkSimulationDBLoad(){
		
		SimulateSET_SET50_CombinedApproach ths = new SimulateSET_SET50_CombinedApproach();
		
		Date[] dateArr = {Date.valueOf("1996-08-30"),
				Date.valueOf("1996-10-14"),
				Date.valueOf("1996-11-28"),
				Date.valueOf("1997-01-12"),
				Date.valueOf("1997-02-26"),
				Date.valueOf("1997-04-12"),
				Date.valueOf("1997-05-27"),
				Date.valueOf("1997-07-11"),
				Date.valueOf("1997-08-25"),
				Date.valueOf("1997-10-09"),
				Date.valueOf("1997-11-23"),
				Date.valueOf("1998-01-07"),
				Date.valueOf("1998-02-21"),
				Date.valueOf("1998-04-07"),
				Date.valueOf("1998-05-22"),
				Date.valueOf("1998-07-06"),
				Date.valueOf("1998-08-20"),
				Date.valueOf("1998-10-04"),
				Date.valueOf("1998-11-18"),
				Date.valueOf("1999-01-02"),
				Date.valueOf("1999-02-16"),
				Date.valueOf("1999-04-02"),
				Date.valueOf("1999-05-17"),
				Date.valueOf("1999-07-01"),
				Date.valueOf("1999-08-15"),
				Date.valueOf("1999-09-29"),
				Date.valueOf("1999-11-13"),
				Date.valueOf("1999-12-28"),
				Date.valueOf("2000-02-11"),
				Date.valueOf("2000-03-27"),
				Date.valueOf("2000-05-11"),
				Date.valueOf("2000-06-25"),
				Date.valueOf("2000-08-09"),
				Date.valueOf("2000-09-23"),
				Date.valueOf("2000-11-07"),
				Date.valueOf("2000-12-22"),
				Date.valueOf("2001-02-05"),
				Date.valueOf("2001-03-22"),
				Date.valueOf("2001-05-06"),
				Date.valueOf("2001-06-20"),
				Date.valueOf("2001-08-04"),
				Date.valueOf("2001-09-18"),
				Date.valueOf("2001-11-02"),
				Date.valueOf("2001-12-17"),
				Date.valueOf("2002-01-31"),
				Date.valueOf("2002-03-17"),
				Date.valueOf("2002-05-01"),
				Date.valueOf("2002-06-15"),
				Date.valueOf("2002-07-30"),
				Date.valueOf("2002-09-13"),
				Date.valueOf("2002-10-28"),
				Date.valueOf("2002-12-12"),
				Date.valueOf("2003-01-26"),
				Date.valueOf("2003-03-12"),
				Date.valueOf("2003-04-26"),
				Date.valueOf("2003-06-10"),
				Date.valueOf("2003-07-25"),
				Date.valueOf("2003-09-08"),
				Date.valueOf("2003-10-23"),
				Date.valueOf("2003-12-07"),
				Date.valueOf("2004-01-21"),
				Date.valueOf("2004-03-06"),
				Date.valueOf("2004-04-20"),
				Date.valueOf("2004-06-04"),
				Date.valueOf("2004-07-19"),
				Date.valueOf("2004-09-02"),
				Date.valueOf("2004-10-17"),
				Date.valueOf("2004-12-01"),
				Date.valueOf("2005-01-15"),
				Date.valueOf("2005-03-01"),
				Date.valueOf("2005-04-15"),
				Date.valueOf("2005-05-30"),
				Date.valueOf("2005-07-14"),
				Date.valueOf("2005-08-28"),
				Date.valueOf("2005-10-12"),
				Date.valueOf("2005-11-26"),
				Date.valueOf("2006-01-10"),
				Date.valueOf("2006-02-24"),
				Date.valueOf("2006-04-10"),
				Date.valueOf("2006-05-25"),
				Date.valueOf("2006-07-09"),
				Date.valueOf("2006-08-23"),
				Date.valueOf("2006-10-07"),
				Date.valueOf("2006-11-21"),
				Date.valueOf("2007-01-05"),
				Date.valueOf("2007-02-19"),
				Date.valueOf("2007-04-05"),
				Date.valueOf("2007-05-20"),
				Date.valueOf("2007-07-04"),
				Date.valueOf("2007-08-18"),
				Date.valueOf("2007-10-02"),
				Date.valueOf("2007-11-16"),
				Date.valueOf("2007-12-31"),
				Date.valueOf("2008-02-14"),
				Date.valueOf("2008-03-30"),
				Date.valueOf("2008-05-14"),
				Date.valueOf("2008-06-28"),
				Date.valueOf("2008-08-12"),
				Date.valueOf("2008-09-26"),
				Date.valueOf("2008-11-10"),
				Date.valueOf("2008-12-25"),
				Date.valueOf("2009-02-08"),
				Date.valueOf("2009-03-25"),
				Date.valueOf("2009-05-09"),
				Date.valueOf("2009-06-23"),
				Date.valueOf("2009-08-07"),
				Date.valueOf("2009-09-21"),
				Date.valueOf("2009-11-05"),
				Date.valueOf("2009-12-20"),
				Date.valueOf("2010-02-03"),
				Date.valueOf("2010-03-20"),
				Date.valueOf("2010-05-04"),
				Date.valueOf("2010-06-18"),
				Date.valueOf("2010-08-02"),
				Date.valueOf("2010-09-16"),
				Date.valueOf("2010-10-31"),
				Date.valueOf("2010-12-15"),
				Date.valueOf("2011-01-29"),
				Date.valueOf("2011-03-15"),
				Date.valueOf("2011-04-29"),
				Date.valueOf("2011-06-13"),
				Date.valueOf("2011-07-28"),
				Date.valueOf("2011-09-11"),
				Date.valueOf("2011-10-26"),
				Date.valueOf("2011-12-10"),
				Date.valueOf("2012-01-24"),
				Date.valueOf("2012-03-09"),
				Date.valueOf("2012-04-23"),
				Date.valueOf("2012-06-07"),
				Date.valueOf("2012-07-22"),
				Date.valueOf("2012-09-05")};
		
		int count=0, totalCount=0;
		
		
		//Save the date array to output object for serialization.
		for (Date dt:dateArr){
			SimulationOutputData.DATE_ARRAY.add(dt);
		}
		
    	String cacheKey = "databaseconnection";
		// the first loop of c is to count the number of total case for ETA calculation
		for(int c=0;c<2;c++){

			for(int i=0;i<2;i++){
				double MaxPcntMaxLossOnETF = -0.50, StartPcntMaxLossOnETF = -0.05;
				if(i==0){
					SimulateSET_SET50_CombinedApproach.PLAY_SET50_ETF_ON_LONG = false;
					MaxPcntMaxLossOnETF = StartPcntMaxLossOnETF;
				}else{
					SimulateSET_SET50_CombinedApproach.PLAY_SET50_ETF_ON_LONG = true;
				}
				for(float takeProfitThreashold = 0.05f; takeProfitThreashold <= 0.5f; takeProfitThreashold = takeProfitThreashold + 0.05f){
					for(double pcntMaxLossOnETF = StartPcntMaxLossOnETF ; pcntMaxLossOnETF >= MaxPcntMaxLossOnETF; pcntMaxLossOnETF = pcntMaxLossOnETF - 0.05){
						for(int cappedContract = 1; cappedContract <= 1024;){
							//TODO: split the simulation to 3 parts, remaining now is from 10 to 20
							for(int smav = 3; smav <= 11; smav++){
								for(double minRunBal = 50000; minRunBal <= 100000; minRunBal = minRunBal + 25000){
									for(double pcntMaxLossOnBal = -0.1 ; pcntMaxLossOnBal >= -0.50; pcntMaxLossOnBal = pcntMaxLossOnBal - 0.1){
										for(double lossStopPerLot = -5000; lossStopPerLot >= -100000; lossStopPerLot = lossStopPerLot - 5000){
											
											ArrayList<SimulationData> sims = new ArrayList<SimulationData>();
											for (Date dt:dateArr){
												
												if(c==0){
													totalCount++;
												}else{
													SimulateSET_SET50_CombinedApproach.PERCENT_MAX_LOSS_ON_BALANCE_SET50_ETF = pcntMaxLossOnETF;
													SimulateSET_SET50_CombinedApproach.CAPPED_NUMBER_OF_CONTRACT = cappedContract;
													SimulateSET_SET50_CombinedApproach.START_DATE = dt;
													SimulateSET_SET50_CombinedApproach.SMAV = smav;
													SimulateSET_SET50_CombinedApproach.LOSS_STOPPING_PER_CONTRACT = lossStopPerLot;
													SimulateSET_SET50_CombinedApproach.PERCENT_MAX_LOSS_ON_BALANCE_TFEX = pcntMaxLossOnBal;
													SimulateSET_SET50_CombinedApproach.MINIMUM_RUNNING_BALANCE = minRunBal;
													SimulateSET_SET50_CombinedApproach.TAKE_PROFIT_THRESHOLD = takeProfitThreashold;
													
													ths = new SimulateSET_SET50_CombinedApproach();

													SimulationData simDat = new SimulationData();
													ths.execute(false);

													try{
//														simDat.setStartDate(ths.simulationDataArray.get(0).getStartDate());
														simDat.setStartDate(dt);
														simDat.setEndDate(ths.simulationDataArray.get(ths.simulationDataArray.size() - 1).getEndDate());
														simDat.setBalance(ths.simulationDataArray.get(ths.simulationDataArray.size() - 1).getBalance());
//														simDat.setEndIndex(getApproxEquivalentInterestPerYear(INITIAL_CAPITAL, simDat.getBalance_asDouble(), Days.daysBetween(new DateTime(ths.simulationDataArray.get(0).getStartDate()), new DateTime(ths.simulationDataArray.get(ths.simulationDataArray.size() - 1).getEndDate())).getDays()));
													}catch(IndexOutOfBoundsException e){
														
													}
													sims.add(simDat);
													
													if(count % 10000 == 0){
														if(count < 30000){
															System.out.println(new Timestamp(System.currentTimeMillis()).toString() + " ; " + numberFormat.format(count) + " of " + numberFormat.format(totalCount));
														}else if(count >= 30000 && count < 60000){
															SIMPLE_REGRESSION.addData(count, System.currentTimeMillis());
															System.out.println(new Timestamp(System.currentTimeMillis()).toString() + " ; " + numberFormat.format(count) + " of " + numberFormat.format(totalCount));
														}else{
															if(count % 100000 == 0)
																SIMPLE_REGRESSION.addData(count, System.currentTimeMillis());
															double m = SIMPLE_REGRESSION.getSlope();
															double b = SIMPLE_REGRESSION.getIntercept();
															long y = (long)(m * totalCount + b);
															System.out.println(new Timestamp(System.currentTimeMillis()).toString() + " ; " + numberFormat.format(count) + " of " + numberFormat.format(totalCount) + " ; ETA=" + new Timestamp(y).toString());
														}
														
														if(count > 1){
															try {
																getConnection().commit();
															} catch (SQLException e) {
																e.printStackTrace();
																throw new RuntimeException("SQL Error : " + e.getMessage());
															}
														}
													}
													count++;
												}
												
											}

											if(c!=0){
												
												String type;
												if(PLAY_SET50_ETF_ON_LONG){
													type = "ETF";
												}else{
													type = "TFEX";
												}
												
												long conditionId;
												try {
													conditionId = insertSimConditionTable(
															getConnection(),
															type, 
															cappedContract, 
															minRunBal, 
															pcntMaxLossOnBal, 
															pcntMaxLossOnETF, 
															lossStopPerLot, 
															smav,
															takeProfitThreashold);

													insertSimDataTable(
															getConnection(),
															conditionId, 
															sims);

												} catch (SQLException e) {
													e.printStackTrace();
													throw new RuntimeException("SQL Error : " + e.getMessage());
												}
												
											}

										}
									}
								}
							}
							
							if(cappedContract == 1){
								cappedContract++;
							}else{
								cappedContract = (int) Math.pow(2, cappedContract);
							}
						}
					}
				}
			}

		}
		
		try {
			getConnection().commit();
		} catch (SQLException e) {
			e.printStackTrace();
			throw new RuntimeException("SQL Error : " + e.getMessage());
		}
//		SimulationOutputData.unload("/tmp/simulation.dat");
		
	}
	
	private static long insertSimConditionTable(Connection conn, String type, int cappedContract, double minRunBal,
			double pcntMaxLossOnBal, double pcntMaxLossOnETF, double lossStopPerLot, int smav2, double takeProfitThreashold) {
		
		long conditionId = 0;
		try {
			
			PreparedStatement pstmt = conn.prepareStatement(
					"SELECT CONDITION_ID " +
					"  FROM SIMULATION_CONDITION" +
					" WHERE SIMULATION_TYPE = ?" +
					" AND CAPPED_CONTRACT_NUMBER = ?" +
					" AND MINIMUM_BALANCE = ?" +
					" AND PERCENT_MAX_LOSS_ON_BALANCE = ?" +
					" AND PERCENT_MAX_LOSS_ON_ETF = ?" +
					" AND LOSS_STOP_PER_LOT = ?" +
					" AND SMAV = ?" +
					" AND PROFIT_TAKING_THRES = ?");
			
			pstmt.setString(1, type);
			pstmt.setInt(2, cappedContract);
			pstmt.setDouble(3, minRunBal);
			pstmt.setDouble(4, pcntMaxLossOnBal);
			pstmt.setDouble(5, pcntMaxLossOnETF);
			pstmt.setDouble(6, lossStopPerLot);
			pstmt.setInt(7, smav2);
			pstmt.setDouble(8, takeProfitThreashold);
			
			ResultSet rs = pstmt.executeQuery();
			
			if(rs.next()){
				conditionId = rs.getLong(1);
			}else{
				
				rs.close();
				pstmt.close();
				
				conditionId = SimulationCommonUtils.getNewConditionId(conn);
				
				pstmt = conn.prepareStatement(
						"INSERT INTO SIMULATION_CONDITION " +
						"VALUES(?,?,?,?,?,?,?,?,?)");
				
				pstmt.setLong(1, conditionId);
				pstmt.setString(2, type);
				pstmt.setInt(3, cappedContract);
				pstmt.setDouble(4, minRunBal);
				pstmt.setDouble(5, pcntMaxLossOnBal);
				pstmt.setDouble(6, pcntMaxLossOnETF);
				pstmt.setDouble(7, lossStopPerLot);
				pstmt.setInt(8, smav2);
				pstmt.setDouble(9, takeProfitThreashold);
				pstmt.executeUpdate();
				
				pstmt.close();
				
			}
			
		} catch (SQLException e) {
			e.printStackTrace();
			throw new RuntimeException("SQL Error : " + e.getMessage());
		}
		
		return conditionId;
	}

	private static void insertSimDataTable(Connection conn, long conditionId, ArrayList<SimulationData> sims) {
		
		try {
			
			PreparedStatement pstmt = conn.prepareStatement(
					"INSERT INTO SIMULATION_OUTPUT " +
					"VALUES(?,?,?)");
			
			((OraclePreparedStatement)pstmt).setExecuteBatch (sims.size());
			
			for(SimulationData sim:sims){
				
				pstmt.setLong(1, conditionId);
				pstmt.setDate(2, sim.getStartDate());
				pstmt.setFloat(3, sim.getBalance());
				
				pstmt.executeUpdate();
			}
			
			pstmt.close();
			
		} catch (SQLException e) {
			e.printStackTrace();
			throw new RuntimeException("SQL Error : " + e.getMessage());
		}
	}
	
	private static void printSimulationOutputByCodnitions(){
		
		Connection conn;
		long[] conditions = {1509,
				1529,
				1549,
				1569,
				1589};
		
		Date[] dateArr = { Date.valueOf("1996-08-30"), Date.valueOf("1996-10-14"), Date.valueOf("1996-11-28"),
				Date.valueOf("1997-01-12"), Date.valueOf("1997-02-26"), Date.valueOf("1997-04-12"), Date.valueOf("1997-05-27"),
				Date.valueOf("1997-07-11"), Date.valueOf("1997-08-25"), Date.valueOf("1997-10-09"), Date.valueOf("1997-11-23"),
				Date.valueOf("1998-01-07"), Date.valueOf("1998-02-21"), Date.valueOf("1998-04-07"), Date.valueOf("1998-05-22"),
				Date.valueOf("1998-07-06"), Date.valueOf("1998-08-20"), Date.valueOf("1998-10-04"), Date.valueOf("1998-11-18"),
				Date.valueOf("1999-01-02"), Date.valueOf("1999-02-16"), Date.valueOf("1999-04-02"), Date.valueOf("1999-05-17"),
				Date.valueOf("1999-07-01"), Date.valueOf("1999-08-15"), Date.valueOf("1999-09-29"), Date.valueOf("1999-11-13"),
				Date.valueOf("1999-12-28"), Date.valueOf("2000-02-11"), Date.valueOf("2000-03-27"), Date.valueOf("2000-05-11"),
				Date.valueOf("2000-06-25"), Date.valueOf("2000-08-09"), Date.valueOf("2000-09-23"), Date.valueOf("2000-11-07"),
				Date.valueOf("2000-12-22"), Date.valueOf("2001-02-05"), Date.valueOf("2001-03-22"), Date.valueOf("2001-05-06"),
				Date.valueOf("2001-06-20"), Date.valueOf("2001-08-04"), Date.valueOf("2001-09-18"), Date.valueOf("2001-11-02"),
				Date.valueOf("2001-12-17"), Date.valueOf("2002-01-31"), Date.valueOf("2002-03-17"), Date.valueOf("2002-05-01"),
				Date.valueOf("2002-06-15"), Date.valueOf("2002-07-30"), Date.valueOf("2002-09-13"), Date.valueOf("2002-10-28"),
				Date.valueOf("2002-12-12"), Date.valueOf("2003-01-26"), Date.valueOf("2003-03-12"), Date.valueOf("2003-04-26"),
				Date.valueOf("2003-06-10"), Date.valueOf("2003-07-25"), Date.valueOf("2003-09-08"), Date.valueOf("2003-10-23"),
				Date.valueOf("2003-12-07"), Date.valueOf("2004-01-21"), Date.valueOf("2004-03-06"), Date.valueOf("2004-04-20"),
				Date.valueOf("2004-06-04"), Date.valueOf("2004-07-19"), Date.valueOf("2004-09-02"), Date.valueOf("2004-10-17"),
				Date.valueOf("2004-12-01"), Date.valueOf("2005-01-15"), Date.valueOf("2005-03-01"), Date.valueOf("2005-04-15"),
				Date.valueOf("2005-05-30"), Date.valueOf("2005-07-14"), Date.valueOf("2005-08-28"), Date.valueOf("2005-10-12"),
				Date.valueOf("2005-11-26"), Date.valueOf("2006-01-10"), Date.valueOf("2006-02-24"), Date.valueOf("2006-04-10"),
				Date.valueOf("2006-05-25"), Date.valueOf("2006-07-09"), Date.valueOf("2006-08-23"), Date.valueOf("2006-10-07"),
				Date.valueOf("2006-11-21"), Date.valueOf("2007-01-05"), Date.valueOf("2007-02-19"), Date.valueOf("2007-04-05"),
				Date.valueOf("2007-05-20"), Date.valueOf("2007-07-04"), Date.valueOf("2007-08-18"), Date.valueOf("2007-10-02"),
				Date.valueOf("2007-11-16"), Date.valueOf("2007-12-31"), Date.valueOf("2008-02-14"), Date.valueOf("2008-03-30"),
				Date.valueOf("2008-05-14"), Date.valueOf("2008-06-28"), Date.valueOf("2008-08-12"), Date.valueOf("2008-09-26"),
				Date.valueOf("2008-11-10"), Date.valueOf("2008-12-25"), Date.valueOf("2009-02-08"), Date.valueOf("2009-03-25"),
				Date.valueOf("2009-05-09"), Date.valueOf("2009-06-23"), Date.valueOf("2009-08-07"), Date.valueOf("2009-09-21"),
				Date.valueOf("2009-11-05"), Date.valueOf("2009-12-20"), Date.valueOf("2010-02-03"), Date.valueOf("2010-03-20"),
				Date.valueOf("2010-05-04"), Date.valueOf("2010-06-18"), Date.valueOf("2010-08-02"), Date.valueOf("2010-09-16"),
				Date.valueOf("2010-10-31"), Date.valueOf("2010-12-15"), Date.valueOf("2011-01-29"), Date.valueOf("2011-03-15"),
				Date.valueOf("2011-04-29"), Date.valueOf("2011-06-13"), Date.valueOf("2011-07-28"), Date.valueOf("2011-09-11"),
				Date.valueOf("2011-10-26"), Date.valueOf("2011-12-10"), Date.valueOf("2012-01-24"), Date.valueOf("2012-03-09"),
				Date.valueOf("2012-04-23"), Date.valueOf("2012-06-07"), Date.valueOf("2012-07-22"), Date.valueOf("2012-09-05") };
		
		try {
			conn = getConnection();
			
			for(long condition:conditions){
				PreparedStatement ps = conn.prepareStatement(
						" select SIMULATION_TYPE" +
						" 		,CAPPED_CONTRACT_NUMBER" +
						" 		,MINIMUM_BALANCE" +
						" 		,PERCENT_MAX_LOSS_ON_BALANCE" +
						" 		,PERCENT_MAX_LOSS_ON_ETF" +
						" 		,LOSS_STOP_PER_LOT" +
						" 		,SMAV" +
						" 		,PROFIT_TAKING_THRES" +
						"  FROM SIMULATION_CONDITION" +
						" WHERE CONDITION_ID = ?" 
						);
				
				ps.setLong(1, condition);
				ResultSet rs = ps.executeQuery();
				if(rs.next()){
					
					System.out.print(rs.getString(1) + "|");
					System.out.print(rs.getInt(2) + "|");
					System.out.print(rs.getInt(3) + "|");
					System.out.print(rs.getDouble(4) + "|");
					System.out.print(rs.getDouble(5) + "|");
					System.out.print(rs.getInt(6) + "|");
					System.out.print(rs.getInt(7) + "|");
					System.out.print(rs.getDouble(8));
					
					for(Date dt:dateArr){
						PreparedStatement ps2 = conn.prepareStatement(
								"SELECT BALANCE" +
								"  FROM SIMULATION_OUTPUT" +
								" WHERE CONDITION_ID = ?" +
								"   AND START_DATE = ?");
						ps2.setLong(1, condition);
						ps2.setDate(2, dt);
						
						ResultSet rs2 = ps2.executeQuery();
						
						if(rs2.next()){
							System.out.print("|" + numberFormat.format(rs2.getDouble("BALANCE")));
						}else{
							System.out.print("|0");
						}
						
						rs2.close();
						ps2.close();
					}
					System.out.println("");
				}
				
				rs.close();
				ps.close();
			}
			
		} catch (SQLException e) {
			e.printStackTrace();
			throw new RuntimeException("SQL Error : " + e.getMessage());
		}
	}

	private static void printBulkSimulationOutput(){
		
		Connection conn;
		try {
			conn = getConnection();
			
			PreparedStatement ps = conn.prepareStatement(
					" select SIMULATION_TYPE" +
							" ,CAPPED_CONTRACT_NUMBER" +
							" ,MINIMUM_BALANCE" +
							" ,PERCENT_MAX_LOSS_ON_BALANCE" +
							" ,PERCENT_MAX_LOSS_ON_ETF" +
							" ,LOSS_STOP_PER_LOT" +
							" ,SMAV" +
							" ,count(*)" +
							" from SIMULATION_OUTPUT" +
							" where balance < 500000" +
							" group by SIMULATION_TYPE" +
							" ,CAPPED_CONTRACT_NUMBER" +
							" ,MINIMUM_BALANCE" +
							" ,PERCENT_MAX_LOSS_ON_BALANCE" +
							" ,PERCENT_MAX_LOSS_ON_ETF" +
							" ,LOSS_STOP_PER_LOT" +
							" ,SMAV");
			
			ResultSet rs = ps.executeQuery();
			
			while(rs.next()){
				if(rs.getInt(8) < 10){
					System.out.print(rs.getString(1) + "|");
					System.out.print(rs.getInt(2) + "|");
					System.out.print(rs.getInt(3) + "|");
					System.out.print(rs.getDouble(4) + "|");
					System.out.print(rs.getDouble(5) + "|");
					System.out.print(rs.getInt(6) + "|");
					System.out.print(rs.getInt(7) + "|");
					System.out.print(rs.getInt(8) + "|");
					
					PreparedStatement ps2 = conn.prepareStatement(
							" select avg(balance)" +
							" from simulation_output" +
							" where simulation_type = ?" +
							" and capped_contract_number = ?" +
							" and minimum_balance = ?" +
							" and percent_max_loss_on_balance = ?" +
							" and percent_max_loss_on_etf = ?" +
							" and loss_stop_per_lot = ?" +
							" and smav = ?" +
							" group by SIMULATION_TYPE" +
							" ,CAPPED_CONTRACT_NUMBER" +
							" ,MINIMUM_BALANCE" +
							" ,PERCENT_MAX_LOSS_ON_BALANCE" +
							" ,PERCENT_MAX_LOSS_ON_ETF" +
							" ,LOSS_STOP_PER_LOT" +
							" ,SMAV");
					
					ps2.setString(1,rs.getString(1));
					ps2.setInt(2,rs.getInt(2));
					ps2.setInt(3,rs.getInt(3));
					ps2.setDouble(4,rs.getDouble(4));
					ps2.setDouble(5,rs.getDouble(5));
					ps2.setInt(6,rs.getInt(6));
					ps2.setInt(7,rs.getInt(7));
					
					ResultSet rs2 = ps2.executeQuery();
					
					if(rs2.next()){
						System.out.print(rs2.getDouble(1));
					}
					
					System.out.println("");
					
					rs2.close();
					ps2.close();
				}
			}
			
		} catch (SQLException e) {
			e.printStackTrace();
			throw new RuntimeException("SQL Error : " + e.getMessage());
		}
		
//		SimulationOutputData.load("/tmp/simulation.dat");
//		
//		//Print header
//		System.out.print(
//				"TradeType" +
//				";cappedContract" +
//				";MinBalance" +
//				";PercentMaxLossOnBal" +
//				";PercentMaxLossOnETF" +
//				";LossStopPerLot" +
//				";SMAV");
//		for (Date dt:SimulationOutputData.DATE_ARRAY){
//			System.out.print(";" + dt.toString());
//		}
//		System.out.println("");
//		
//		Iterator<String> itrStr = SimulationOutputData.SIMULATION_OUTPUT.keySet().iterator();
//		while(itrStr.hasNext()){
//			String key = itrStr.next();
//			ArrayList<SimulationData> sims = SimulationOutputData.SIMULATION_OUTPUT.get(key);
//			System.out.print(key);
//			for(SimulationData sim:sims){
//				System.out.print(";" + numberFormat.format(sim.getBalance()));
//			}
//			System.out.println("");
//		}
	}
	
	private static Connection getConnection() throws SQLException{

		Connection conn = (Connection) GlobalCache.getCache(serialVersionUID);

		if (conn == null) {
			conn = DriverManager.getConnection("jdbc:oracle:thin:@172.16.115.131:1521/finance", "finance", "finance");
//			conn = DriverManager.getConnection("jdbc:oracle:thin:@192.168.1.248:1521/finance", "finance", "finance");
//			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "finance", "finance");
			conn.setAutoCommit(false);
			GlobalCache.addCache(serialVersionUID, conn);
		}
		
		return conn;
		
	}
	
	public void execute(boolean printDetailedSimulation){
		
		boolean isInvested = false;

		double runningBalance = INITIAL_CAPITAL;
		
        try {
			
        	Connection conn = getConnection();

//			if(weeklyData == null
//					|| (weeklyData != null && SimulateSET_SET50_CombinedApproach.SMAV != weeklyData.get(weeklyData.firstKey()).getSMAV())){
//				weeklyData = loadMarketDataFromDB(conn);
//			}
			weeklyData = SimulationCommonUtils.loadMarketDataFromDB(conn, SMAV, "EFIN", "SET", "WEEKLY");
			
			Iterator<DateTime> itrWeekly = weeklyData.keySet().iterator();
			MarketData thisWeek = null;
			
			SimulationData simulationItem = null;
			
			if(itrWeekly.hasNext()){
				thisWeek = weeklyData.get(itrWeekly.next());
			}

			DateTime stopDate = new DateTime(START_DATE).plusYears(SIMULATION_LENGTH_IN_YEAR).plusMonths(3);
			DateTime currDate = new DateTime(thisWeek.getDate());


			//Start looping through the weekly data to find a start-point, either to short or buy SET50
			while(itrWeekly.hasNext()
					&& currDate.isBefore(stopDate.getMillis())){
				
				while(itrWeekly.hasNext()
						&& (currDate.isBefore(START_DATE.getTime()) || currDate.isEqual(START_DATE.getTime()))){
					//Loop to the start point
					thisWeek = weeklyData.get(itrWeekly.next());
					currDate = new DateTime(thisWeek.getDate());
				}
				
				if( ! isInvested ){

					if(thisWeek.getHighIndex() < thisWeek.getCloseIndexSMAV()){
						//This means the candle stick is under the SMAV line
						simulationItem = new SimulationData();
						simulationItem.setTypeOfTransaction(TransactionType.SHORT_TEFX);
						
						isInvested = true;

					}else if(thisWeek.getLowIndex() > thisWeek.getCloseIndexSMAV()){
						//This means the candle stick is above the SMAV line
						simulationItem = new SimulationData();
						if(PLAY_SET50_ETF_ON_LONG){
							simulationItem.setTypeOfTransaction(TransactionType.BUY_SET50);
						}else{
							simulationItem.setTypeOfTransaction(TransactionType.LONG_TFEX);
						}
						
						isInvested = true;

					}else{
						//This means the candle stick is in the middle of the line, need to skip run
						thisWeek = weeklyData.get(itrWeekly.next());
						currDate = new DateTime(thisWeek.getDate());
					}
					
					if(isInvested){
						
						simulationItem.setStartDate(thisWeek.getDate());
						simulationItem.setStartIndex(getForexClosingIndexOfWeek(thisWeek, conn));

						if(! simulationItem.getTypeOfTransaction().equals(TransactionType.BUY_SET50)){
							double numberOfContractByLossPerContract = Math.abs(runningBalance * PERCENT_MAX_LOSS_ON_BALANCE_TFEX / LOSS_STOPPING_PER_CONTRACT);
							double numberOfContractByMargin = Math.abs(runningBalance / TFEX_SET50_MARGIN_PER_CONTRACT);
							
							if(numberOfContractByMargin < numberOfContractByLossPerContract){
								simulationItem.setNumberOfTFEXContract((int)SimulationCommonUtils.roundOff(numberOfContractByMargin, 0));
							}else{
								simulationItem.setNumberOfTFEXContract((int)SimulationCommonUtils.roundOff(numberOfContractByLossPerContract, 0));
							}
							
							if(simulationItem.getNumberOfTFEXContract() <= 0){
								simulationItem.setNumberOfTFEXContract(1);
							}else if(simulationItem.getNumberOfTFEXContract() > CAPPED_NUMBER_OF_CONTRACT){
								simulationItem.setNumberOfTFEXContract(CAPPED_NUMBER_OF_CONTRACT);
							}
						}
						
						thisWeek = weeklyData.get(itrWeekly.next());
						currDate = new DateTime(thisWeek.getDate());
					}
					
				}else{
					
					double maxLossPerLot = getMaxProfitOrLossPerLotDuringWeek(true, simulationItem, thisWeek, runningBalance, conn);
					double maxLoss = maxLossPerLot;
					
					if(!simulationItem.getTypeOfTransaction().equals(TransactionType.BUY_SET50)){
						maxLoss = simulationItem.getNumberOfTFEXContract() * maxLossPerLot;
					}
					
					if(maxLoss < simulationItem.getMaximumPointLossForPeriod()){
						simulationItem.setMaximumPointLossForPeriod(maxLoss);
					}
					
					double maxGain = getMaxProfitOrLossPerLotDuringWeek(false, simulationItem, thisWeek, runningBalance, conn);
					if(!simulationItem.getTypeOfTransaction().equals(TransactionType.BUY_SET50)){
						maxGain = maxGain * simulationItem.getNumberOfTFEXContract();
					}

					if(maxGain > simulationItem.getMaximumPointGainForPeriod()){
						simulationItem.setMaximumPointGainForPeriod(maxGain);
					}
					
					if((hasToStopLoss(simulationItem, maxLossPerLot, runningBalance)
								&& dateOfMaxLossForTheWeek.before(dateOfMaxGainForTheWeek)
								&& maxGain/runningBalance >= TAKE_PROFIT_THRESHOLD
								&& TAKE_PROFIT)
							||(hasToStopLoss(simulationItem, maxLossPerLot, runningBalance)
									&& simulationItem.getCutLossAmount() == 0
											&& (maxGain/runningBalance < TAKE_PROFIT_THRESHOLD
													|| ! TAKE_PROFIT))){
						//Stop the loss
						double lossAmount = 0;
						if(simulationItem.getTypeOfTransaction().equals(TransactionType.BUY_SET50)){
							lossAmount = PERCENT_MAX_LOSS_ON_BALANCE_SET50_ETF * runningBalance;
						}else{
							lossAmount = LOSS_STOPPING_PER_CONTRACT * simulationItem.getNumberOfTFEXContract();
						}
						simulationItem.setCutLossDate(thisWeek.getDate());
						simulationItem.setCutLossAmount(lossAmount);
						runningBalance = runningBalance + lossAmount;
						simulationItem.setBalance(runningBalance);
						
						if(CONTINUOUS_INVESTING){
							simulationItem.setEndDate(thisWeek.getDate());
							simulationItem.setEndIndex(getForexClosingIndexOfWeek(thisWeek, conn));
							simulationDataArray.add(simulationItem);
							isInvested = false;
							thisWeek = weeklyData.get(itrWeekly.next());
							currDate = new DateTime(thisWeek.getDate());
						}
					
					}else if(TAKE_PROFIT
							&& maxGain/runningBalance >= TAKE_PROFIT_THRESHOLD
							&& simulationItem.getCutLossAmount() == 0){
						
						simulationItem.setTakeProfit(true);
						simulationItem.setEndDate(thisWeek.getDate());
						runningBalance = runningBalance + (TAKE_PROFIT_THRESHOLD * runningBalance);
						simulationItem.setBalance(runningBalance);
						
						simulationDataArray.add(simulationItem);
						isInvested = false;
						
						thisWeek = weeklyData.get(itrWeekly.next());
						currDate = new DateTime(thisWeek.getDate());

					}else if(hasCutSMAV(simulationItem, thisWeek)){
						
						simulationItem.setEndDate(thisWeek.getDate());
						simulationItem.setEndIndex(getForexClosingIndexOfWeek(thisWeek, conn));
						
						if(simulationItem.getCutLossAmount_asDouble() == 0){
							double gainLoss = getProfitOrLossPerLotAtEndOfWeek(simulationItem, thisWeek, runningBalance, conn);
							if(!simulationItem.getTypeOfTransaction().equals(TransactionType.BUY_SET50)){
								gainLoss = gainLoss * simulationItem.getNumberOfTFEXContract();
							}
							runningBalance = runningBalance + gainLoss;
							simulationItem.setBalance(runningBalance);
						}
						simulationDataArray.add(simulationItem);
						isInvested = false;
					}else{
						
						thisWeek = weeklyData.get(itrWeekly.next());
						currDate = new DateTime(thisWeek.getDate());
						
					}
					
					//Check if we should quite the market
					if(runningBalance < MINIMUM_RUNNING_BALANCE){
						if(isInvested){
							simulationDataArray.add(simulationItem);
						}
						break;
					}
					
				}
				
			}
			
			if(printDetailedSimulation){
				System.out.println(
						"StartDate;" +
						"StartIndex;" +
						"Type;" +
						"EndDate;" +
						"EndIndex;" +
						"LotToBuy;" +
						"MaxGain;" +
						"MaxLoss;" +
						"CutLossDate;" +
						"CutLossAmt;" +
						"Balance");
				for(SimulationData simulateItem:simulationDataArray){
					System.out.println(
							simulateItem.getStartDate() + ";" + 
							simulateItem.getStartIndex() + ";"
							+ simulateItem.getTypeOfTransaction() + ";"
							+ simulateItem.getEndDate() + ";" 
							+ simulateItem.getEndIndex() + ";" 
							+ simulateItem.getNumberOfTFEXContract() + ";"
							+ simulateItem.getMaximumPointGainForPeriod() + ";"
							+ simulateItem.getMaximumPointLossForPeriod() + ";"
							+ simulateItem.getCutLossDate() + ";" 
							+ numberFormat.format(simulateItem.getCutLossAmount()) + ";" 
							+ numberFormat.format(simulateItem.getBalance()));
				}
				
				System.out.println("================ Done ================");
			}
			
//			conn.rollback();
//			conn.close();
			
		} catch (SQLException e) {
			e.printStackTrace();
			throw new RuntimeException("SQL Error : " + e.getMessage());
		}
	}
	
	private boolean hasToStopLoss(SimulationData simulationItem, double maxLossPerLot, double runningBalance) {
		if(simulationItem.getCutLossAmount_asDouble() == 0
							&& ((!simulationItem.getTypeOfTransaction().equals(TransactionType.BUY_SET50) && maxLossPerLot < LOSS_STOPPING_PER_CONTRACT)
									|| simulationItem.getTypeOfTransaction().equals(TransactionType.BUY_SET50) && maxLossPerLot < (PERCENT_MAX_LOSS_ON_BALANCE_SET50_ETF * runningBalance))){
			return true;
		}else{
			return false;
		}
	}

	private boolean hasCutSMAV(SimulationData simulationItem, MarketData thisWeek) {
		
		if(simulationItem.getTypeOfTransaction().equals(TransactionType.LONG_TFEX)
				|| simulationItem.getTypeOfTransaction().equals(TransactionType.BUY_SET50)){
			if(thisWeek.getHighIndex() < thisWeek.getCloseIndexSMAV()){
				return true;
			}
		}else if(simulationItem.getTypeOfTransaction().equals(TransactionType.SHORT_TEFX)){
			if(thisWeek.getLowIndex() > thisWeek.getCloseIndexSMAV()){
				return true;
			}
		}
		return false;
	}

	/**
	 * 
	 * @param getLoss - true means to get max loss, otherwise get max profit
	 * @param simData
	 * @param thisWeek
	 * @param conn
	 * @return
	 * @throws SQLException
	 */
	private double getMaxProfitOrLossPerLotDuringWeek(boolean getLoss, SimulationData simData, MarketData thisWeek, double runningBalance, Connection conn) throws SQLException{
		
		double output = 0;
		
		long lowestCacheKey =900000000000000l + thisWeek.getDate().getTime();
		long highestCacheKey = 910000000000000l + thisWeek.getDate().getTime();
		long lowestDateCacheKey = 920000000000000l + thisWeek.getDate().getTime();
		long highestDateCacheKey = 930000000000000l + thisWeek.getDate().getTime();
		
		Double lowestDuringWeek = (Double) GlobalCache.getCache(lowestCacheKey);
		Double highestDuringWeek = (Double) GlobalCache.getCache(highestCacheKey);
		Date lowestDate = (Date) GlobalCache.getCache(lowestDateCacheKey);
		Date highestDate = (Date) GlobalCache.getCache(highestDateCacheKey);
		
		if(lowestDuringWeek == null
				|| highestDuringWeek == null){
			
			PreparedStatement pstmt = conn.prepareStatement(
					"SELECT MIN(LOW), MAX(HIGH)" +
					"  FROM PRICE_HIST " +
					" WHERE TRADING_DATE >= ?" +
					"   AND TRADING_DATE < ?" +
					"   AND CURRENCY_PAIR = 'SET50'" +
					"   AND TRADING_TYPE = 'DAILY'" +
					"   AND MARKET_NAME = 'EFIN'");
			
			pstmt.setDate(1, thisWeek.getDate());
			pstmt.setDate(2, thisWeek.getNextDate());
			
			ResultSet rs = pstmt.executeQuery();
			
			if(rs.next()){
				
				highestDuringWeek = rs.getDouble(2);
				lowestDuringWeek = rs.getDouble(1);
				
			}

			rs.close();
			pstmt.close();
			
			if(highestDuringWeek == 0 || lowestDuringWeek == 0){
				throw new RuntimeException("Something is wrong, highest or lowest during the week is ZERO");
			}
			
			GlobalCache.addCache(highestCacheKey, highestDuringWeek);
			GlobalCache.addCache(lowestCacheKey, lowestDuringWeek);
			
			if(lowestDate == null
					|| highestDate == null){
				PreparedStatement ps = conn.prepareStatement(
						"SELECT T.TRADING_DATE" +
						"  FROM PRICE_HIST T" +
						" WHERE T.TRADING_DATE >=?" +
						"   AND T.TRADING_DATE < ?" +
						"   AND T.LOW = ?" +
						"   AND CURRENCY_PAIR = 'SET50'" +
						"   AND TRADING_TYPE = 'DAILY'" +
						"   AND MARKET_NAME = 'EFIN'" +
						" ORDER BY 1");
				
				ps.setDate(1, thisWeek.getDate());
				ps.setDate(2, thisWeek.getNextDate());
				ps.setDouble(3, lowestDuringWeek);
				
				rs = ps.executeQuery();
				
				if(rs.next()){
					lowestDate = rs.getDate(1);
				}
				
				if(lowestDate == null){
					throw new RuntimeException(
							"Error Can't find lowest date for " + thisWeek.getDate().toString() + " with low point at " + lowestDuringWeek);
				}else{
					GlobalCache.addCache(lowestDateCacheKey, lowestDate);
				}
				
				rs.close();
				ps.close();
				
				ps = conn.prepareStatement(
						"SELECT T.TRADING_DATE" +
						"  FROM PRICE_HIST T" +
						" WHERE T.TRADING_DATE >=?" +
						"   AND T.TRADING_DATE < ?" +
						"   AND T.HIGH = ?" +
						"   AND CURRENCY_PAIR = 'SET50'" +
						"   AND TRADING_TYPE = 'DAILY'" +
						"   AND MARKET_NAME = 'EFIN'" +
						" ORDER BY 1");
				
				ps.setDate(1, thisWeek.getDate());
				ps.setDate(2, thisWeek.getNextDate());
				ps.setDouble(3, highestDuringWeek);
				
				rs = ps.executeQuery();
				
				if(rs.next()){
					highestDate = rs.getDate(1);
				}
				
				if(highestDate == null){
					throw new RuntimeException(
							"Error Can't find highest date for " + thisWeek.getDate().toString() + " with high point at " + highestDuringWeek);
				}else{
					GlobalCache.addCache(highestDateCacheKey, highestDate);
				}
				
				rs.close();
				ps.close();
			}
				
		}
		
		if(getLoss){
			if(simData.getTypeOfTransaction().equals(TransactionType.LONG_TFEX)){
				output = (lowestDuringWeek - simData.getStartIndex()) * TFEX_PRICE_PER_POINT;
				dateOfMaxLossForTheWeek = lowestDate;
			}else if(simData.getTypeOfTransaction().equals(TransactionType.SHORT_TEFX)){
				output = (simData.getStartIndex() - highestDuringWeek) * TFEX_PRICE_PER_POINT;
				dateOfMaxLossForTheWeek = highestDate;
			}else if(simData.getTypeOfTransaction().equals(TransactionType.BUY_SET50)){
				output = (lowestDuringWeek - simData.getStartIndex())/simData.getStartIndex() * runningBalance;
				dateOfMaxLossForTheWeek = lowestDate;
			}
		}else{
			if(simData.getTypeOfTransaction().equals(TransactionType.LONG_TFEX)){
				output = (highestDuringWeek - simData.getStartIndex()) * TFEX_PRICE_PER_POINT;
				dateOfMaxGainForTheWeek = highestDate;
			}else if(simData.getTypeOfTransaction().equals(TransactionType.SHORT_TEFX)){
				output = (simData.getStartIndex() - lowestDuringWeek) * TFEX_PRICE_PER_POINT;
				dateOfMaxGainForTheWeek = lowestDate;
			}else if(simData.getTypeOfTransaction().equals(TransactionType.BUY_SET50)){
				output = (simData.getStartIndex() - lowestDuringWeek)/simData.getStartIndex() * runningBalance;
				dateOfMaxGainForTheWeek = highestDate;
			}
		}
		
		
		return output;
	}
	
	private double getForexClosingIndexOfWeek(MarketData thisWeek, Connection conn) throws SQLException{
		
		long cacheKey = 950000000000000l + thisWeek.getDate().getTime();
		
		Double closeIndex = (Double) GlobalCache.getCache(cacheKey);
		
		if(closeIndex != null){
			return closeIndex;
		}
		
		PreparedStatement pstmt = conn.prepareStatement(
				"SELECT CLOSE" +
				"  FROM PRICE_HIST " +
				" WHERE TRADING_DATE = ( SELECT MAX(TRADING_DATE)" +
				"						   FROM PRICE_HIST" +
				"						  WHERE CURRENCY_PAIR = 'SET50'" +
				"						    AND MARKET_NAME = 'EFIN'" +
				"							AND TRADING_DATE < ?" +
				"							AND TRADING_TYPE = 'DAILY'" +
				"                      )" +
				"   AND CURRENCY_PAIR = 'SET50'" +
				"   AND TRADING_TYPE = 'DAILY'" +
				"   AND MARKET_NAME = 'EFIN'");
		
		pstmt.setDate(1, thisWeek.getNextDate()); 
		
		ResultSet rs = pstmt.executeQuery();
		
		if(rs.next()){
			closeIndex = rs.getDouble("CLOSE");
		}
		
		rs.close();
		pstmt.close();
		
		GlobalCache.addCache(cacheKey, closeIndex);
		return closeIndex;
	}
	
	private double getProfitOrLossPerLotAtEndOfWeek(SimulationData simData, MarketData thisWeek, double runningBalance, Connection conn) throws SQLException{
		
		double output = 0;
		double closeIndex = getForexClosingIndexOfWeek(thisWeek, conn);

		if(simData.getTypeOfTransaction().equals(TransactionType.LONG_TFEX)){
			output = (closeIndex - simData.getStartIndex())  * TFEX_PRICE_PER_POINT;
		}else if(simData.getTypeOfTransaction().equals(TransactionType.SHORT_TEFX)){
			output = (simData.getStartIndex() - closeIndex) * TFEX_PRICE_PER_POINT;
		}else if(simData.getTypeOfTransaction().equals(TransactionType.BUY_SET50)){
			output = (closeIndex - simData.getStartIndex()) / simData.getStartIndex() * runningBalance; 
		}
		
		return output;
	}
	
	private static double getApproxEquivalentInterestPerYear(double initialCapital, double endedBalance, long days){
		
		int capPeriod = 180;
		double initInterest = 0.001f;
		double newEndedBalance = initialCapital;
		
		boolean reverseCalc = false;
		if(endedBalance < initialCapital){
			reverseCalc = true;
			double tmpFlt = initialCapital;
			initialCapital = endedBalance;
			endedBalance = tmpFlt;
		}
		
		long loopControl = 0;
		
		while(Math.abs((endedBalance - newEndedBalance)/endedBalance) > 0.003){
			
			int i=1;
			newEndedBalance = initialCapital;
			do{
				newEndedBalance = newEndedBalance * (1 + (initInterest / 2));
				i = i+capPeriod;
			}while(i <= days);
			
			initInterest += 0.0001f;
			loopControl++;
			if(loopControl > 4000000){
				throw new RuntimeException("Something wrong with interest calculation!!!");
			}
		}
		
		if(reverseCalc){
			return (-1 * initInterest);
		}else{
			return initInterest;
		}
	}
	
}
